<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="QA7cC" id="QA7cC"><span data-lake-id="u22bb7ef3" id="u22bb7ef3">典型回答</span></h1>
  <p data-lake-id="udeb71790" id="udeb71790"><br></p>
  <p data-lake-id="u4c19e0ff" id="u4c19e0ff"><strong><span data-lake-id="u93c4b788" id="u93c4b788">Ribbon是一种客户端负载均衡的解决方案，</span></strong><span data-lake-id="uf7e6a863" id="uf7e6a863">它通常与Spring Cloud一起使用，以在微服务架构中实现负载均衡。</span></p>
  <p data-lake-id="u5e6bff9b" id="u5e6bff9b"><span data-lake-id="u5c6f0008" id="u5c6f0008">​</span><br></p>
  <p data-lake-id="u3f7fbea0" id="u3f7fbea0"><span data-lake-id="ue49fdff7" id="ue49fdff7">客户端负载均衡器的实现原理是通过注册中心，如 Nacos，将可用的服务列表拉取到本地（客户端），再通过客户端负载均衡器（设置的负载均衡策略）获取到某个服务器的具体 ip 和端口，然后再通过 Http 框架请求服务并得到结果。</span></p>
  <p data-lake-id="u2c2936ab" id="u2c2936ab"><span data-lake-id="u962a541d" id="u962a541d">​</span><br></p>
  <p data-lake-id="u2992fa41" id="u2992fa41"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1683878166235-0fca0c8a-624e-4ec0-a906-6eed33ef387c.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_31%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="ud8e4a3cb" id="ud8e4a3cb"><br></p>
  <p data-lake-id="ued25fabf" id="ued25fabf"><span data-lake-id="uc455fb1c" id="uc455fb1c">Ribbon通过在客户端中添加拦截器来实现负载均衡。当客户端发出请求时，拦截器会根据一组预定义的规则选择一个服务实例来处理请求。这些规则可以基于多个因素进行选择，包括服务实例的可用性、响应时间、负载等级等。</span></p>
  <p data-lake-id="u01cb470d" id="u01cb470d"><span data-lake-id="udbb80eb8" id="udbb80eb8">​</span><br></p>
  <p data-lake-id="u8e1fc9a4" id="u8e1fc9a4"><span data-lake-id="u11c62264" id="u11c62264">Ribbon的核心是</span><strong><span data-lake-id="ue8f2e123" id="ue8f2e123">负载均衡算法</span></strong><span data-lake-id="u91d26988" id="u91d26988">，它决定了如何选择服务实例。Ribbon提供了多种负载均衡算法，包括轮询、随机、加权随机、加权轮询、最少连接数等，他们的具体实现是实现了IRule接口，继承自AbstractLoadBalanceRule实现的：</span></p>
  <p data-lake-id="u203791d0" id="u203791d0"><span data-lake-id="u26f3196b" id="u26f3196b">​</span><br></p>
  <p data-lake-id="ud69f0a0c" id="ud69f0a0c"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1683884330518-d0f60d7c-4974-4cb5-9484-bc0e7b09ee08.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_22%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="ube4f67f2" id="ube4f67f2"><br></p>
  <ul list="ue0158105">
   <li fid="u613f0575" data-lake-id="ucef2e0e9" id="ucef2e0e9"><span data-lake-id="ub2652ba0" id="ub2652ba0">轮询策略：RoundRobinRule</span></li>
  </ul>
  <ul list="ue0158105" data-lake-indent="1">
   <li fid="u613f0575" data-lake-id="u5e81c7be" id="u5e81c7be"><span data-lake-id="u4ca02142" id="u4ca02142">按照一定的顺序依次调用服务实例。比如一共有 3 个服务，依次调用服务 1、服务 2、服务3</span></li>
  </ul>
  <ul list="ue0158105" start="2">
   <li fid="u613f0575" data-lake-id="uecaf978d" id="uecaf978d"><span data-lake-id="ue1ff8fbc" id="ue1ff8fbc">权重策略：WeightedResponseTimeRule</span></li>
  </ul>
  <ul list="ue0158105" data-lake-indent="1">
   <li fid="u613f0575" data-lake-id="uae7c7bb9" id="uae7c7bb9"><span data-lake-id="ua1da58d3" id="ua1da58d3">每个服务提供者的响应时间分配一个权重，响应时间越长，权重越小。 它的实现原理是，刚开始使用轮询策略并开启一个计时器，每一段时间收集一次所有服务提供者的平均响应时间，然后再给每个服务提供者附上一个权重，权重越高被选中的概率也越大。</span></li>
  </ul>
  <ul list="ue0158105" start="3">
   <li fid="u613f0575" data-lake-id="ua5db9244" id="ua5db9244"><span data-lake-id="ub5c0ce02" id="ub5c0ce02">随机策略：RandomRule</span></li>
  </ul>
  <ul list="ue0158105" data-lake-indent="1">
   <li fid="u613f0575" data-lake-id="uec8f0a14" id="uec8f0a14"><span data-lake-id="ub1f5d02e" id="ub1f5d02e">这种实现很简单，从服务提供者的列表中随机选择一个服务实例。</span></li>
  </ul>
  <ul list="ue0158105" start="4">
   <li fid="u613f0575" data-lake-id="u2b413c2f" id="u2b413c2f"><span data-lake-id="ue92f8466" id="ue92f8466">最小连接数策略：BestAvailableRule</span></li>
  </ul>
  <ul list="ue0158105" data-lake-indent="1">
   <li fid="u613f0575" data-lake-id="u3ef9b1f9" id="u3ef9b1f9"><span data-lake-id="uc4212319" id="uc4212319">遍历服务提供者列表，选取连接数最小的⼀个服务实例。如果有相同的最小连接数，那么会调用轮询策略进行选取。</span></li>
  </ul>
  <ul list="ue0158105" start="5">
   <li fid="u613f0575" data-lake-id="uc1bedf78" id="uc1bedf78"><span data-lake-id="u0c110144" id="u0c110144">重试策略：RetryRule</span></li>
  </ul>
  <ul list="ue0158105" data-lake-indent="1">
   <li fid="u613f0575" data-lake-id="u5fc9d811" id="u5fc9d811"><span data-lake-id="u375add8f" id="u375add8f">按照轮询策略来获取服务，如果获取的服务实例为 null 或已经失效，则在指定的时间之内不断地进行重试来获取服务，如果超过指定时间依然没获取到服务实例则返回 null。</span></li>
  </ul>
  <ul list="ue0158105" start="6">
   <li fid="u613f0575" data-lake-id="uf868a0b6" id="uf868a0b6"><span data-lake-id="u3c2aa4f8" id="u3c2aa4f8">可用敏感性策略：AvailabilityFilteringRule</span></li>
  </ul>
  <ul list="ue0158105" data-lake-indent="1">
   <li fid="u613f0575" data-lake-id="u64968e2c" id="u64968e2c"><span data-lake-id="u15dc9aa9" id="u15dc9aa9">先过滤掉非健康的服务实例，然后再选择连接数较小的服务实例。</span></li>
  </ul>
  <ul list="ue0158105" start="7">
   <li fid="u613f0575" data-lake-id="ufe45540d" id="ufe45540d"><span data-lake-id="u72be7274" id="u72be7274">区域敏感策略：ZoneAvoidanceRule</span></li>
  </ul>
  <ul list="ue0158105" data-lake-indent="1">
   <li fid="u613f0575" data-lake-id="u0c57b787" id="u0c57b787"><span data-lake-id="u1b742325" id="u1b742325">根据服务所在区域（zone）的性能和服务的可用性来选择服务实例，在没有区域的环境下，该策略和轮询策略类似。</span></li>
  </ul>
  <p data-lake-id="uc3f2baa9" id="uc3f2baa9"><span data-lake-id="udbb33036" id="udbb33036">​</span><br></p>
  <p data-lake-id="ufe735b85" id="ufe735b85"><span data-lake-id="ubabb980f" id="ubabb980f">​</span><br></p>
  <p data-lake-id="ufaa94295" id="ufaa94295"><span data-lake-id="ubb77795f" id="ubb77795f">Ribbon还提供了一些高级功能，如服务列表的动态刷新、失败重试、请求重试、请求超时控制等。这些功能可以帮助客户端更好地适应动态的服务拓扑，并提高系统的可用性和容错性。</span></p>
  <p data-lake-id="ua58fa148" id="ua58fa148"><br></p>
  <h1 data-lake-id="CmvAq" id="CmvAq"><span data-lake-id="u806d5464" id="u806d5464">扩展知识</span></h1>
  <p data-lake-id="u69641bf6" id="u69641bf6"><br></p>
  <h2 data-lake-id="sba9y" id="sba9y"><span data-lake-id="u8635a834" id="u8635a834">Ribbon结合Nacos使用</span></h2>
  <p data-lake-id="ue6db449c" id="ue6db449c"><br></p>
  <p data-lake-id="u16cf16bb" id="u16cf16bb"><u><span data-lake-id="u0b583de6" id="u0b583de6">但是需要注意的是，Nacos 2021 移除了Ribbon的支持，取而代之的是spring-cloud-loadbalancer</span></u></p>
  <p data-lake-id="u6da31840" id="u6da31840"><br></p>
  <p data-lake-id="uc09a9dc5" id="uc09a9dc5"><span data-lake-id="ucb5cd4d0" id="ucb5cd4d0">1、POM依赖增加对Nacos和Ribbon的依赖：</span></p>
  <p data-lake-id="u209570ba" id="u209570ba"><span data-lake-id="u4489e1b0" id="u4489e1b0">​</span><br></p>
  <pre lang="java"><code>
&lt;dependency&gt;
    &lt;groupId&gt;com.alibaba.cloud&lt;/groupId&gt;
    &lt;artifactId&gt;spring-cloud-starter-alibaba-nacos-discovery&lt;/artifactId&gt;
    &lt;version&gt;2.2.5.RELEASE&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
    &lt;artifactId&gt;spring-cloud-starter-netflix-ribbon&lt;/artifactId&gt;
    &lt;version&gt;2.2.10.RELEASE&lt;/version&gt;
&lt;/dependency&gt;

</code></pre>
  <p data-lake-id="ud839fb6a" id="ud839fb6a"><br></p>
  <p data-lake-id="ue4cedb20" id="ue4cedb20"><span data-lake-id="ua7d545fd" id="ua7d545fd">在Spring Boot应用程序中配置Nacos注册中心：</span></p>
  <p data-lake-id="udee1312f" id="udee1312f"><br></p>
  <pre lang="java"><code>
spring:
  cloud:
    nacos:
      discovery:
        server-addr: ${NACOS_SERVER_ADDR:localhost:8848}

</code></pre>
  <p data-lake-id="u702629a5" id="u702629a5"><br></p>
  <p data-lake-id="ubb7c981d" id="ubb7c981d"><span data-lake-id="u67854a9e" id="u67854a9e">在需要调用远程服务的类中注入Ribbon Client：</span></p>
  <p data-lake-id="ubcb5ab88" id="ubcb5ab88"><span data-lake-id="u3a32d349" id="u3a32d349">​</span><br></p>
  <pre lang="java"><code>
@RestController
public class MyController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/invoke")
    public String invoke() {
        // 发送请求并获取响应
        String response = restTemplate.getForObject("http://my-service/my-api", String.class);
        return response;
    }
}
</code></pre>
  <p data-lake-id="uf68f99c7" id="uf68f99c7"><br></p>
  <p data-lake-id="u140977cf" id="u140977cf"><span data-lake-id="ucf78c79a" id="ucf78c79a">在应用程序启动类中配置Ribbon Client：</span></p>
  <p data-lake-id="u73edd9c6" id="u73edd9c6"><span data-lake-id="ua2c19c56" id="ua2c19c56">​</span><br></p>
  <pre lang="java"><code>
@Configuration
public class RibbonConfig {

    @Bean
    public IRule ribbonRule() {
        // 使用随机算法选择服务实例
        return new RandomRule();
    }

    @Bean
    @LoadBalanced	//使用@LoadBalanced注解来启用负载均衡功能。
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder.build();
    }
}

</code></pre>
  <p data-lake-id="ub53ebb4f" id="ub53ebb4f"><br></p>
  <h2 data-lake-id="SOhpu" id="SOhpu" style="text-align: justify"><span data-lake-id="ufb9190f5" id="ufb9190f5" style="color: rgb(18, 18, 18)">Ribbon工作流程</span></h2>
  <p data-lake-id="u5fcfaa3f" id="u5fcfaa3f"><span data-lake-id="u78c0e942" id="u78c0e942">Ribbon的工作流程大致如下：</span></p>
  <p data-lake-id="u1e006932" id="u1e006932"><span data-lake-id="ue3d5610b" id="ue3d5610b">​</span><br></p>
  <ol list="ue4973340">
   <li fid="u0ce002a4" data-lake-id="u0cfc12f1" id="u0cfc12f1" data-lake-index-type="true"><span data-lake-id="u41e2d0f4" id="u41e2d0f4">服务提供者先将自己注册到Nacos、Eureka等注册中心中。</span></li>
   <li fid="u0ce002a4" data-lake-id="u6517ead9" id="u6517ead9" data-lake-index-type="true"><span data-lake-id="u6b928fc9" id="u6b928fc9">客户端向Ribbon发送请求。Ribbon将请求的目标服务名称和API路径解析出来。</span></li>
   <li fid="u0ce002a4" data-lake-id="u8b448f40" id="u8b448f40" data-lake-index-type="true"><span data-lake-id="u98057576" id="u98057576">Ribbon根据服务名称去注册中心（如Eureka、Consul、Nacos等）获取可用的服务实例列表。</span></li>
   <li fid="u0ce002a4" data-lake-id="uc0bf7beb" id="uc0bf7beb" data-lake-index-type="true"><span data-lake-id="u8ba61944" id="u8ba61944">Ribbon通过负载均衡算法（如轮询、随机、加权随机、最少活跃数等）从实例列表中选择一台目标服务器，将请求转发给目标服务器。</span></li>
   <li fid="u0ce002a4" data-lake-id="u1dd49ea9" id="u1dd49ea9" data-lake-index-type="true"><span data-lake-id="ue1ba25ab" id="ue1ba25ab">目标服务器处理请求并返回响应给客户端。</span></li>
   <li fid="u0ce002a4" data-lake-id="u24325008" id="u24325008" data-lake-index-type="true"><span data-lake-id="u3afda455" id="u3afda455">客户端接收到响应后进行处理。</span></li>
  </ol>
  <p data-lake-id="u4af2eaed" id="u4af2eaed"><br></p>
  <p data-lake-id="uaa8d5b3c" id="uaa8d5b3c"><strong><span data-lake-id="u3bfe25ae" id="u3bfe25ae">​</span></strong><br></p>
  <p data-lake-id="u72b3b037" id="u72b3b037"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1683885945887-5a170460-5d9a-4a82-83ea-87fa0ad1c632.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_37%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
 </body>
</html>